🤖 feat: Agent Plugins install/update UX (managed installs, v1) - #3820
🤖 feat: Agent Plugins install/update UX (managed installs, v1)#3820ThomasK33 wants to merge 60 commits into
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3b9d7245ce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Addressed all five Codex findings in b9c9a10:
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9c9a1062a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Addressed all four round-2 findings in edcdfa0:
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edcdfa05fe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Addressed all three round-3 findings:
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6503d2fe4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bc68e771d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a2bd105f4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0443e1af3e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dc141c4c7e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6daf6aec0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f1fd47e8cd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6965bc298
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e576538423
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…er snapshot cannot re-enable a sibling-reinstalled plugin server
|
@codex review Round 66 fix (9e113da):
Thread resolved; |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e113da27c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…t-serve disk read, cap hooks.js source size in discovery)
|
@codex review Round 67 fixes (6a708bf):
Both threads resolved; |
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: 6a708bf773
Only the user who started this review can view the report in Codex.
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6a708bf773
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ches the consented top-level surface
|
@codex review Round 68 fix (908adb5):
Thread resolved; |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 908adb52ab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ead, release failed update trash for reclamation, fail update on epoch publication failure)
|
@codex review Round 69 fixes (4b6d9be):
All three threads resolved; |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b6d9be3dd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…kills, release unlink retry, update recovery provenance reconciliation, CLI registration sanitization, fresh-install override sweep)
|
@codex review Round 70 fixes (35b8e89):
All five threads resolved; |
readHookSourceCapped's open follows symlinks: a managed update replacing a consented regular hooks.js with an absolute link outside the plugin root (allowed by staged validation, read as a capability removal by discovery) could have the stale canonical pathname follow the new link and evaluate an outside file as hook code. Require the opened object to be the regular file a non-following lstat sees at the path (dev/ino identity, bigint stats).
|
@codex review Round 71 fix (aac2280):
|
There was a problem hiding this comment.
💡 Codex Review
When a top-level plugin workflow is resolved concurrently with a managed update, containment can validate the old regular workflows/foo.js, after which promotion replaces that pathname before this separate read. The replacement may be an escaping absolute symlink that stable-tree discovery would reject, but readFileString follows it anyway; a link to an outside named pipe hangs the workflow indefinitely, while a link to another JavaScript file executes source outside the consented plugin tree. Read through a non-following handle and verify the same opened object that passed containment and the size check.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Security Review
Here are some automated security review suggestions for this pull request.
Reviewed commit: aac2280844
Only the user who started this review can view the report in Codex.
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
…x round 72 P2) xum run and xum workflow construct AgentSession directly, bypassing WorkspaceService.createSession, so the optional sanitizeCliWorkspaceRegistration callback was silently skipped and a preserved checkout could carry a stale plugin: MCP enable into a same-name reinstall on the first CLI send. Pass the sanitizer at both headless entry points.
|
@codex review Round 72 fix (3bca011):
|
…me (Codex round 73 P2) The leaf lstat identity check cannot catch a replacement symlink at an ancestor component (lib/hooks.js where lib becomes a link to an outside dir): lstat follows ancestor links, so the outside file reports as regular with matching dev/ino. readHookSourceCapped now also rechecks full symlink-resolved containment against the plugin root after the open; a promotion is a single swap, so a link the open followed is still present at the recheck.
|
@codex review Round 73 fix (d59e33e):
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3bca011dd8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d59e33e4b2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| `mcp.json is too large (${stat.size} bytes; max ${MAX_PLUGIN_MANIFEST_BYTES})` | ||
| ); | ||
| } | ||
| raw = JSON.parse(await fsPromises.readFile(plugin.mcpConfigPath, "utf8")) as unknown; |
There was a problem hiding this comment.
Revalidate the MCP config handle before parsing
When an enabled managed plugin server is updated so its new mcp.json is an absolute symlink to an existing file outside the plugin root, staged discovery treats MCP as removed and permits the update; if runtime discovery captured the old canonical path before promotion, this later readFile follows that path into the replacement tree and parses the outside configuration. An attacker-controlled file can preserve the enabled server name while changing it to a bare command such as sh, which is spawned before the mutation-epoch post-check can retire the stale result, yielding command execution during the race. Read through a bounded handle and revalidate containment plus file identity before using the configuration.
AGENTS.md reference: AGENTS.md:L136-L136
Useful? React with 👍 / 👎.
| // freshly started server would otherwise publish validly and keep | ||
| // running from the removed tree. This runs BEFORE override pruning so | ||
| // pruning problems cannot skip the correctness-critical invalidation. | ||
| await this.deps.mcpServerManager?.stopServersWithKeyPrefix(serverKeyPrefix); |
There was a problem hiding this comment.
Retire registered hooks when uninstall commits
When a workspace has an active agent stream while its managed plugin is uninstalled, this post-removal invalidation closes only MCP servers; the plugin's already-registered AgentPluginHookService middleware remains live until another send calls ensureWorkspaceHooks or the workspace is disposed. The current stream can therefore continue exposing later tool arguments/results to, and accepting rewrites or denials from, a hook after uninstall has reported success. Explicitly invalidate affected workspace hook registrations at commit time, including in sibling processes via the mutation epoch.
AGENTS.md reference: AGENTS.md:L150-L150
Useful? React with 👍 / 👎.
…(Codex round 74 P2) Both CLI entry points omit workspaceMcpOverridesService, and only the desktop ServiceContainer called setWorkspaceMcpOverridesService, so the CLI registration sanitizer early-returned on the undefined service and a stale checkout-local plugin: enable survived headless registration. createCoreServices now default-constructs the overrides service, passes it to AIService and the plugin-invalidation override reader, and wires WorkspaceService pruning for every process that can register workspaces; the desktop's now-redundant explicit set call is removed.
|
@codex review Round 74 fix (f98dd52):
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f98dd52dc5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // override reads AND registration-time plugin-override sanitization must | ||
| // work in every process that can register workspaces, not just desktop. | ||
| const workspaceMcpOverridesService = | ||
| opts.workspaceMcpOverridesService ?? new WorkspaceMcpOverridesService(config); |
There was a problem hiding this comment.
Preserve live overrides in ephemeral CLI sessions
When xum run or xum workflow is launched inside a checkout already registered by the desktop app, the caller's config points at a temporary directory and explicitly contains no persistent workspace records (src/cli/run.ts:435-470 and src/cli/workflow.ts:204-223). Constructing the override service from that config means the registration sanitizer sees only the newly added ephemeral workspace, cannot detect the persistent live sibling for the same checkout, and prunes every canonical plugin entry from the shared .xum/mcp.local.jsonc; merely invoking the CLI therefore disables the desktop workspace's configured plugin servers and loses its allowlists. The sanitizer needs access to persistent sibling metadata, or a path-based pruning API that separates the ephemeral workspace record from the metadata used for the live-sibling check.
Useful? React with 👍 / 👎.
Summary
v1 of the Agent Plugins install/update UX ("Option B: managed installs"): paste a git URL or
owner/repo[@ref]into Settings → Plugins, get a consent preview of everything the plugin contributes (manifest, every skill, every MCP server command line), and install into~/.mux/pluginswith provenance recorded in a managed-install registry. Update badge + manual update, uninstall with override pruning, all behind the existingagent-pluginsexperiment.Background
PR #3815 shipped Agent Plugins 1.0.0 as discovery-only: users had to
git cloneinto container dirs by hand, with no provenance, no update signal, no uninstall, and no list surface. The design doc (docs/research/agent-plugin-integration-options.mdon branchresearch-agent-plugin-ux) compared five options; Thomas signed off on Option B (managed installs) with the §6 proposed decisions ratified.Approved decisions implemented here
~/.mux/plugins.jsonowned by the install service (atomic, throwing writes; in-process serialized mutations). Lenient-on-read: invalid entries are dropped with a warning, and plugin names are pattern-validated so a malformed entry can never resolve a path outside the container. (Deviation from §6-Q2's letter, following its own contingency: Codex review demonstrated that.passthrough()only affects schema validation — older builds rebuild config.json from known fields on save, so a downgrade would drop an embedded registry section. Q2 priced exactly this: "Cost if wrong: a one-time migration to a separate file." A file older builds never rewrite is the only mechanism that actually survives downgrade round-trips, and owning the write path also makes registry-persistence failures observable for rollback.)source.refis the tracking channel,lockedShais what runs. No ref given ⇒ record the remote default branch + pin its current SHA. Tag/SHA refs are pinned (moved tags surface atag movedwarning badge). Nothing auto-applies, ever.~/.mux/plugin-staging(never inside a discovery container), validated with the samevalidatePluginManifest+ discovery code the runtime uses, listing manifest metadata, every skill name+description, and every MCP command line (rendered against the final install path, incl.PLUGIN_DATAexpansion). Cancelling writes nothing — the preview is stateless; install re-fetches the exact consented SHA and fails loudly if the remote moved.git ls-remotevslockedSha, no fetch, no timers). Applying = temp clone at the new SHA → re-validate → wholesale directory swap (rename-old → promote-new → delete-old, with rollback) → bumplockedSha→ recycle that plugin's running MCP servers via the newMCPServerManager.stopServersWithKeyPrefix(content can change behind an unchanged stdio command line, so the config-signature check cannot notice). Local edits to a managed plugin dir are discarded on update (documented).plugin:<instanceId>:*keys from every local workspace's MCP overrides (reinstall re-attaches the same instanceId, so stale overrides would silently re-enable servers).~/.mux/plugin-data/<instanceId>is preserved behind an "also delete stored plugin data" checkbox, unchecked by default.Settings: Plugins,Install Agent Plugin…,Check for Plugin Updates,Update All Plugins— keyboard rule). No agent-facing installer tool.owner/repo/sub/path[@ref]parses and thesubpathfield is persisted in the source descriptor, but installs reject with "monorepo subpath installs land in v2". Claude Code plugin/marketplace repos fail with a clear message naming the limitation (source stays a discriminated union so an import adapter is additive).Implementation
src/common/config/schemas/agentPluginInstalls.ts(entry + tagged-union source +plugins.jsonfile schema); name grammar shared with the manifest validator viasrc/common/utils/agentPluginName.ts.discoverAgentPluginAt(public single-root wrapper over the existing per-entry discovery, so staged clones get the exact runtime validation);normalizeRepoUrlForCloneextracted tosrc/node/utils/gitUrls.ts(shared with the project clone flow);sourceInput.tsgrammar;AgentPluginInstallService(preview/install/list/uninstall/checkUpdates/update, mutations serialized on an internal queue, staging under~/.mux/plugin-stagingwith stale-dir reclamation,GIT_TERMINAL_PROMPT=0+ SSH BatchMode so private repos without auth fail fast instead of hanging);plugins.*oRPC namespace returningResultvalues;MCPServerManager.stopServersWithKeyPrefixrecycle hook. Backend gating mirrors the MCP provider: the service is constructed withisEnabled: () => experimentsService.isExperimentEnabled(AGENT_PLUGINS).PluginsSettingsSection(list withunmanaged/missing/update available/tag moved/pinnedbadges, two-phase add flow, inline uninstall confirm), experiment-gated section registration + redirect + palette entry.docs/config/mcp-servers.mdx+docs/agents/agent-skills.mdx; Storybook stories with play assertions (consent preview, update states, unchecked-by-default checkbox); unit tests for the input grammar, registry round-trip/self-heal, and the full service lifecycle against real local git remotes (hermetic — local-path remotes exercise the same clone/ls-remote plumbing).Validation
make static-checkgreen (typecheck, ESLint, prettier, docs links); targeted suites: 393 tests across the touched areas (agentPlugins, config, schemas, SettingsPage, palette sources, MCPServerManager, oRPC router, projectService) all pass;test-storybookpasses for the new stories.make dev-server-sandboxinstance (screenshots in the workspace transcript): enabled the experiment via Settings → Experiments (Plugins section appeared immediately), installed a local fixture repo through the full preview → consent → install flow, verified the on-disk registry entry + plugin tree (no.git), advanced the fixture remote →update availablebadge appeared on "Check for updates" → Update bumpedlockedSha/version/updatedAt, uninstall (checkbox unchecked) removed dir + registry but preservedplugin-data, and the reinstalled plugin's MCP server surfaced in Settings → MCP asplugin · …default-disabled/read-only.Risks
~/.mux/plugins.json;config.jsonload/save is untouched. Malformed registry entries degrade to "unmanaged dir" rather than errors; downgrade-safe because older builds never touch the file.stopServersWithKeyPrefixonly stops matching workspaces' server sets; they restart lazily on next use, same as the idle-timeout path. No behavior change for non-plugin servers.agent-pluginsoff, the service throws, the section/palette entry hide, and no new code paths run.Judgement calls
installre-fetches the exact consented SHA (direct SHA fetch, falling back to branch clone + HEAD verification) rather than keeping the preview clone on disk between preview and confirm — a stateless preview means cancel/crash cannot leave partial state, at the cost of a second shallow clone on confirm..git(plain content snapshot): the registry holds all provenance, updates replace the dir wholesale, and a live checkout would only invite in-place edits that updates discard.plugin.json#namechanged): container-entry names are identity (instanceId → PLUGIN_DATA, workspace overrides), so renames require uninstall/reinstall.Update All Pluginsapplies onlyupdate-availableentries; moved tags stay per-plugin manual (a mutated tag deserves the section's warning, not a bulk apply).Deferred (per §5/§6 of the design)
subpathinstalls (sparse checkout; grammar + schema already in place), content-addressed store + symlinked container entries, dev-mode/local-path installs, unmanaged-dir adoption ("convert to managed"),Pinrow action,bun run debug plugin …CLI +/pluginslash command.archive+sha256 / seed dirs for air-gap, restore-from-lock, catalogs/marketplace (Claude marketplace import adapter only on demonstrated demand).autoUpdateboolean reserved in the schema, unused), agent-facing install tool, Claude Code marketplace compatibility.Post-review hardening (Codex rounds 1–20)
20 review rounds of fixes folded into this diff
Highlights beyond the original plan (full round-by-round history in the PR review threads):
plugins.jsonwith lossless raw-document writes (unknown envelope/entry/tombstone fields from newer builds survive rewrites), strict-mode reads for mutations vs lenient reads for list, raw-entry collision checks, non-ENOENT read errors refuse mutations.pendingOverridePrunestombstones (pessimistic commit, retry on list, reconciliation against deleted workspaces, reinstall gate) so a temporarily unreachable checkout can never let a reinstall silently re-enable a pruned server.workspace.mcp.getreturns{ overrides, revision };setrequiresexpectedRevisionand rejects stale dialog snapshots (serialized check-and-set), and the uninstaller's prune retries on conflict — a stale open dialog can no longer resurrect prunedplugin:keys.closeInvalidatedInstancesThenPublishre-scans until the invalidation clock is stable and publishes synchronously in the same continuation, closing the microtask window where a mid-startup plugin swap could publish (and keep alive) a server from a deleted tree. Regression test interleaves the swap into the exact yield window.break-allon plugin name/location/source lines + pinned 390px story with a max-length-name overflow assertion.Two P2 follow-ups are documented (not in this PR) in this comment: stale workspace-switch modal loads, and prefix-stop retry-marker publish ordering.
Rebase + experimental label
main(squashed to one feature commit): merged this branch's install contract with main's independently-landedagentPlugins.tsoRPC schemas (slash commands/composition inspector), re-ported the stable-clock publication onto the MCP SDK v2mcpServerManager, and adopted main's SSH→HTTPS clone fallback into the extractedgitUrls.ts(installer records the primary URL only, documented).FlaskConicalnav icon + in-section warning banner.dev-server-sandbox(install → consent preview incl. a deliberately-invalidmcp.jsondiagnostic → update badge → atomic update → read-only MCP row → uninstall with data-preservation default); screenshots in the workspace transcript.Generated with
mux• Model:anthropic:claude-fable-5• Thinking:xhigh• Cost:$327.63